

//reducer是一个方法 接收当前state action 根据state action的type返回新的state 
export function createStore(reducer,enhancer){
    if(enhancer){
        console.log(enhancer(createStore)(reducer))
        // 这里返回的store是包装过dispatch的store
        return enhancer(createStore)(reducer)
    }
    var ActionTypes={
        INIT: '@@redux/INIT' + Math.random().toString(36).substring(7).split('').join('.'),
    }
    let currentState;//当前的state
    let currentListeners=[];
    function getState(){
        return currentState
    }
    function subscribe(listener){//监听 订阅 
        currentListeners.push(listener)
    }
    function dispatch(action){//dispatch是触发reducer改变state的方法 同时也返回action 执行currentListeners里的listener

        currentState=reducer(currentState,action)
        currentListeners.forEach(v=>v())
        return action
    }
    dispatch({type:ActionTypes.INIT})//默认初始化dispath是可以获取到state的
    return {getState,subscribe,dispatch}

}
export function applyMiddleware(...middlewares){
    return createStore=>(...args)=>{
        const store=createStore(...args);
        let dispatch=store.dispatch;
        // 并没有直接使用dispatch:dispatch，而是使用了dispatch:(action) => dispatch(action)，
        // 其目的是如果使用了dispatch:dispatch，那么在所有的Middleware中实际都引用的同一个dispatch(闭包)，
        // 如果存在一个中间件修改了dispatch，就会导致后面一下一系列的问题，但是如果使用dispatch:(action) => dispatch(action)就可以避免这个问题。
        const midApi={
            getState:store.getState,
            dispatch:(...args)=>dispatch(...args)
        }
        let middlewareChain=[]
        console.log(middlewares)
        middlewareChain=middlewares.map(middleware=>middleware(midApi))
        // f1(f2(args))
        console.log(middlewareChain)
        dispatch=compose(...middlewareChain)(store.dispatch)
        // dispatch=middlewares()(store.dispatch)
        console.log('改造后的dispatch')
        console.log(dispatch)
        return {
            ...store,
            dispatch
        }
    }
}
//addGun=dispatch(addGun()) 返回addGun 已经被dispatch执行过一遍了
function bindActionCteator(creator,dispatch) {
    return (...arg)=>dispatch(creator(...arg))
}
//{addGun,removeGun,addGunAsync}
export function bindActionCreators(creators,dispatch){
    // let bound={};
    // Object.keys(creators).forEach(v=>{
    //     let creator=creators[v];
    //     bound[v]=bindActionCteator(creator,dispatch)
    // })
    // return bound
    return Object.keys(creators).reduce((total,currentValue)=>{
        total[currentValue]=bindActionCteator(creators[currentValue],dispatch)
        return total;
    },{})
    
}

export function compose(...funcs){
    if(funcs.length==0){
        return arg=>arg
    }
    if(funcs.length==1){
        return funcs[0]
    }

    // [f1,f2]
    return funcs.reduce((ret,item)=>(...args)=>ret(item(...args)))
    
}
// f1(f2(f3(dispatch)))
// [f1,f2].reduce((ret,item)=>(...args)=>(item(ret(...args))))

// [f1,f2,f3].reduce(function(ret,item){
//     return function (...args) {
//         return function (...args) {
//             item(ret(...args))
//         }
//     }
// })
// f3(f2(f1(func)))